package com.qinglei.recoup.common.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qinglei.recoup.common.domain.CommonConstant;
import com.qinglei.recoup.common.domain.RadarFallDownDataDTO;
import com.qinglei.recoup.common.domain.RadarMessage;
import com.qinglei.recoup.common.domain.RadarUserDataDTO;
import com.qinglei.recoup.common.exception.RedisConnectException;
import com.qinglei.recoup.common.service.RedisService;
import com.qinglei.recoup.common.utils.JsonUtils;
import com.qinglei.recoup.system.domain.Radar;
import com.qinglei.recoup.system.service.RadarService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 模拟监测仪服务
 */
@Service
@Slf4j
public class TestRadarService implements InitializingBean {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RedisService redisService;

    @Resource
    private RadarService radarService;

    @Value("${recoup.test-message.enable:false}")
    private Boolean isEnable;

    public static final String TEST_MESSAGE_DEVICE_CACHE = "test.message.device.cache";

    public static final String DEFAULT_SN = "11223344";

    @Override
    public void afterPropertiesSet() throws Exception {
        if (!isEnable) {
            return;
        }
        Map<String, String> snMap = redisService.hgetAll(TEST_MESSAGE_DEVICE_CACHE);
        if (snMap == null || snMap.size() == 0) {
            this.addDevice(DEFAULT_SN);
        }
    }

    public void addDevice(String sn) throws RedisConnectException {
        if (!isEnable) {
            return;
        }
        log.info("addDevice sn:{}", sn);
        TestRadarBO testRadarBO = new TestRadarBO();
        testRadarBO.setSn(sn);
        testRadarBO.setStatus(TestRadarBO.STATUS_NORMAL);
        redisService.hset(TEST_MESSAGE_DEVICE_CACHE, testRadarBO.getSn(), JsonUtils.objectToJson(testRadarBO));
        this.sendMessage(testRadarBO.getSn(), RadarMessage.TYPE_DEVICE_STATUS, RadarMessage.STATUS_ONLINE);
    }

    public Boolean delDevice(String sn) throws RedisConnectException {
        if (!isEnable) {
            return Boolean.FALSE;
        }
        log.info("delDevice sn:{}", sn);
        String device = redisService.hget(TEST_MESSAGE_DEVICE_CACHE, sn);
        if (StringUtils.isEmpty(device)) {
            log.warn("sendMessage sn is not exist:{}", sn);
            return Boolean.FALSE;
        }
        redisService.hdel(TEST_MESSAGE_DEVICE_CACHE, sn);
        this.sendMessage(sn, RadarMessage.TYPE_DEVICE_STATUS, RadarMessage.STATUS_OFFLINE);
        return Boolean.TRUE;
    }

    public void change2OutBed(String sn, Integer minutes) throws RedisConnectException {
        if (!isEnable) {
            return;
        }
        log.info("delDevice sn:{}, minutes:{}", sn, minutes);
        String device = redisService.hget(TEST_MESSAGE_DEVICE_CACHE, sn);
        if (StringUtils.isEmpty(device)) {
            log.warn("sendMessage sn is not exist:{}", sn);
            return;
        }
        TestRadarBO testRadarBO = JsonUtils.jsonToBean(device, TestRadarBO.class);
        if (testRadarBO == null) {
            log.warn("sendMessage testRadarBO is null");
            return;
        }
        testRadarBO.setStatus(TestRadarBO.STATUS_OUT_BED);
        testRadarBO.setTimestamp(System.currentTimeMillis() + minutes * 60 *1000);
        redisService.hset(TEST_MESSAGE_DEVICE_CACHE, sn, JsonUtils.objectToJson(testRadarBO));

        this.sendMessage(sn, RadarMessage.TYPE_USER_DATA, TestRadarBO.STATUS_OUT_BED);
    }

    private void sendMessage(String sn, Integer type, Integer status) throws RedisConnectException {
        if (!isEnable) {
            return;
        }
        Radar oneRadar = radarService.getOne(Wrappers.<Radar>lambdaQuery().eq(Radar::getSnCode, sn));
        if (oneRadar == null) {
            return;
        }
        RadarMessage radarMessage = new RadarMessage();
        radarMessage.setSn(sn.toUpperCase());
        radarMessage.setType(type);
        radarMessage.setTimestamp(System.currentTimeMillis());
        if (type == RadarMessage.TYPE_DEVICE_STATUS) {
            radarMessage.setData(status);
        } else {
            if (oneRadar.getType() == null || oneRadar.getType() == 1) {
                RadarUserDataDTO radarUserDataDTO = new RadarUserDataDTO();
                radarUserDataDTO.setBreathBpm(RandomUtils.nextInt(0, 256));
                radarUserDataDTO.setHeartBpm(RandomUtils.nextInt(0, 256));
                int[] heartPhaseData = new int[10];
                int[] breathPhaseData = new int[10];
                int curBreath = RandomUtils.nextInt(0, 300);
                int curHeart = RandomUtils.nextInt(0, 300);
                for (int i = 0; i < 10; i++) {
                    heartPhaseData[i] = curHeart;
                    breathPhaseData[i] = curBreath;
                    if (RandomUtils.nextBoolean()) {
                        curBreath += RandomUtils.nextInt(0, 30);
                    } else {
                        curBreath -= RandomUtils.nextInt(0, 30);
                    }
                    curBreath = Math.max(curBreath, 0);
                    if (RandomUtils.nextBoolean()) {
                        curHeart += RandomUtils.nextInt(0, 30);
                    } else {
                        curHeart -= RandomUtils.nextInt(0, 30);
                    }
                    curHeart = Math.max(curHeart, 0);
                }
                radarUserDataDTO.setBreathPhaseData(breathPhaseData);
                radarUserDataDTO.setHeartPhaseData(heartPhaseData);
                if (status != null && status == TestRadarBO.STATUS_OUT_BED) {
                    radarUserDataDTO.setS2(0);
                } else {
                    radarUserDataDTO.setS2(RandomUtils.nextInt(1, 4));
                }
                radarMessage.setData(radarUserDataDTO);
            } else {
                radarMessage.setType(RadarMessage.TYPE_FALL_DOWN_DATA);
                RadarFallDownDataDTO radarFallDownDataDTO = new RadarFallDownDataDTO();
                radarFallDownDataDTO.setExist(RandomUtils.nextInt(0, 2));
                if (status != null && status == TestRadarBO.STATUS_OUT_BED) {
                    radarFallDownDataDTO.setL2(1);
                } else {
                    radarFallDownDataDTO.setL2(0);
                }
                radarMessage.setData(radarFallDownDataDTO);
            }
        }
        rabbitTemplate.convertAndSend(CommonConstant.MQ_RECOUP_DIRECT_EXCHANGE, CommonConstant.MQ_RADAR_DATA_ROUTING, radarMessage);
    }

    public void runSendMessage() throws RedisConnectException {
        Map<String, String> snMap = redisService.hgetAll(TEST_MESSAGE_DEVICE_CACHE);
        if (snMap == null || snMap.size() == 0) {
            return;
        }
        snMap.forEach((sn, val)->{
            if (StringUtils.isEmpty(val)) {
                log.warn("runSendMessage val is not exist, sn:{}", sn);
                return;
            }
            TestRadarBO testRadarBO = JsonUtils.jsonToBean(val, TestRadarBO.class);
            if (testRadarBO == null) {
                log.warn("runSendMessage testRadarBO is null");
                return;
            }
            try {
                long now = System.currentTimeMillis();
                if (testRadarBO.getStatus() == TestRadarBO.STATUS_OUT_BED && testRadarBO.getTimestamp() < now) {
                    testRadarBO.setStatus(TestRadarBO.STATUS_NORMAL);
                    testRadarBO.setTimestamp(0L);
                    redisService.hset(TEST_MESSAGE_DEVICE_CACHE, sn, JsonUtils.objectToJson(testRadarBO));
                }
                if (testRadarBO.getStatus() == TestRadarBO.STATUS_OUT_BED) {
                    this.sendMessage(sn, RadarMessage.TYPE_USER_DATA, TestRadarBO.STATUS_OUT_BED);
                } else {
                    this.sendMessage(sn, RadarMessage.TYPE_USER_DATA, null);
                }
            } catch (RedisConnectException e) {
                log.error("runSendMessage error", e);
            }
        });
    }
}
